package mapObjects;
//general Unit class
//compare based on relative positioning on map
//this is to allow sorting in order to expedite path finding

import gui.GamePanel;

import java.util.*;
import java.awt.*;

import ai.MapNavigator;

import ai.UnitGroup;

import mapData.Coord;
public abstract class Unit implements Comparable<Unit> 
{
    
    protected String type;
    protected Color color;
    protected int atk;
    protected int range;
    protected int hp;
    protected Coord curr, dest,start;
    protected Coord origDest = null;
    protected int mvspd;
    protected int aspd;
    protected ArrayList<Coord> path = new ArrayList<Coord>();
    private boolean isReady = false;
    private boolean attack_move_mode = true;
    private boolean halted = false;
    private int mvspd_cntr = 0;
    private int aspd_cntr = 0;
    protected Unit enemyTarget = null; //target unit
    protected ArrayList<Unit> targetedBy = new ArrayList<Unit>(5);
    protected ArrayList<Unit> ptrToContainingList;
    protected UnitGroup ptrToCurrGroup = null;
    
    
    
    
    public Unit(){} 
    
    //public Unit(String teamColor, int x, int y);
    
    //public Unit(String teamColor, Coord pos);
    
    public int compareTo(Unit u)
    {
        return curr.compareTo(u.getCoord());
    }
    
    
    
    public String toString()
    {
    	String print;
    	print = type+ " at " + curr;
    	return print;
    }
    
    public void setCurrentGoup(UnitGroup u)
    {
    	ptrToCurrGroup = u;
    }
    
    public void setPath(ArrayList<Coord> p)
    {
    	path = p;
    }
    
    public Coord getDest(){
    	return dest;
    }
    
    public Coord getOrigDest()
    {
    	return origDest;
    }
    
    public String getType()
    {
        return type;
    }
    
    public Color getColor()
    {
        return color;
    }
    
    public int getHp()
    {
        return hp;
    }
    
    public int chngHP(int chng)
    {
        hp += chng;
        return hp;
    }
    
    public Coord getCoord()
    {
        return curr;
    }
    
    
    public void setTarget(Unit targ)
    {
    	enemyTarget = targ;
    	
        dest = targ.getCoord();
        path.clear();
    	
    	targ.setTargetedBy(this);
    	
    }
    
    private void setTargetedBy(Unit prey)
    {
    	targetedBy.add(prey);
    }
    
    
    
    
    public void setDest(int x, int y)
    {
    	this.setDest(new Coord(x, y));
    }
    
    
    public void setDest(Coord targ)
    {
    	start = new Coord(curr);
    	halted = false;
    	isReady = false;
        dest = targ;
        origDest = new Coord(targ.getX(), targ.getY());
        //path.clear();
        untarget();
       
    }
    
    
    
    public void resumeMovement()
    {
    	isReady = true;
    	halted = false;
    	
    }
    
    public void forceClearPath()
    {
    	path.clear();
    }
    
    public void haltMovement()
    {
    	isReady = false;
    	halted = true;
    }
    
    
    
    public void move()
    {
    	
    	if(attack_move_mode)   		
            attackRandomInRange();
    	
    	if(halted || curr.equals(dest))
    		return;
    	
    	mvspd_cntr+=mvspd;
    	if(mvspd_cntr< 10)
    	{
    		return;
    	}
    	mvspd_cntr = 0;
    	
    	if(path.size() == 0 && isReady)
    	{
    		Coord poten = GamePanel.pfinder.getBestNeighborUsingHeurstic(start, curr, dest);
    		if(!poten.equals(curr))
    		{
    			
    			curr = poten;
    			return;
    		}
    		else
    		{
    			path = GamePanel.pfinder.bfsPath(curr, dest);
    			
    		}
    	}
    		
    	
      
        if(!curr.isEqual(dest) && path.size()>0)
        {
        	
        	if(!GamePanel.pfinder.isEmpty(dest) && GamePanel.pfinder.manhattanDistance(curr, dest) < 7) //if destination is occupied
        	{
        		getNewAlternateDest(); //get new dest
        		forceClearPath(); //clear current path 
        		return;
        	}
        	if(!GamePanel.pfinder.isEmpty(path.get(0))) //if blocked, gen new path to dest
        	{
        		path = GamePanel.pfinder.bfsPath(curr, dest); 
        	}
        	
        	if(path.size() ==0) return;
        	
            curr = path.remove(0);
            
            
            
        }
        
        
        
    } 
    
    //function determines arrangement of group movement after 1 unit reaches dest coord
    public void getNewAlternateDest(){
    	
    	dest = GamePanel.pfinder.findClosestValidTile(origDest);
    	
    	
    }
    
    
    
    //damage nearby target
    private void damageTarget(Unit target)
    {
    	aspd_cntr += aspd;
		if(aspd_cntr<20) return;
			aspd_cntr = 0;
        target.chngHP(atk*(-1));
        System.out.println(target + "dealt "+ atk +" damage by " +this);
        
        if(target.hp<=0)
        {
        	target.removeSelfFromGame();
        	System.out.println("Target destroyed");
        	
        }
    }
    
    
    //attack random enemy in range
    public void attackRandomInRange()
    {
    	ArrayList<Unit> list = GamePanel.pfinder.getEnemyUnitsInRange(range, curr, this.color);
    	
    	if(list.size()>0 && enemyTarget==null)
    	{
    		haltMovement();
 
	    	try
	    	{
		    	int chosen = (int)(Math.random()*list.size());
		    	damageTarget(list.get(chosen));
	    	}
	    	catch(IndexOutOfBoundsException e)
	    	{
	    		return;
	    	}
	    	
	    	
    	}
    	else
    	{
    		if(halted)
    		resumeMovement();
    	}
    	
    	
    	
    }
    
   
    
    public void attackSetTarget()
    {
    	
    	if(enemyTarget != null)
    	{
    		
	    		if(GamePanel.pfinder.manhattanDistance(curr, enemyTarget.getCoord()) <= range) //if in range
	    		{
	    			haltMovement();
	    			damageTarget(enemyTarget);
	    		}
	    		else
	    		{
	    			resumeMovement();
	    		}
	    	
    	}
    }
    
    //might cause npe later on
    public void removeSelfFromGame()
    {
	if(ptrToCurrGroup != null)
    																																ptrToCurrGroup.removeUnit(this);
    	ptrToContainingList.remove(this);
    	for(int i = 0; i < targetedBy.size(); i++)
    	{
    		targetedBy.get(i).enemyTarget = null;
    	}
    	targetedBy.clear();
    }
    
    private void untarget()
    {
    	if(enemyTarget != null)
    	enemyTarget.targetedBy.remove(this);
    	enemyTarget = null;
    }
    
}